﻿namespace NetFx.SMIL.Definitions
{
  using System;
  using System.Text;
  using System.Collections;
  using System.Collections.Generic;

  using System.Reflection;
  using System.Reflection.Emit;
  using System.Runtime.InteropServices;

  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Diagnostics.SymbolStore;
  using System.Security;
  using System.Security.Permissions;

  using NetFx.MSIL;
  using NetFx.SMIL.References;

  public class PropertyDef:MemberDef
  {
    #region Statics
    public static PropertyDef of(PropertyInfo propertyInfo)
    {
      return new PropertyDef(propertyInfo);
    }
    public static PropertyDef of(string name)
    {
      return new PropertyDef(name);
    }
    #endregion
    #region Fields
    protected PropertyAttributes attributes;
    protected TypeRef propertyType;
    protected MethodRef getter;
    protected MethodRef setter;
    protected object defaultValue;
    protected List<TypeRef> optionalCustomModifiers = null;
    protected List<TypeRef> requiredCustomModifiers = null;
    protected List<ParameterDef> parameters = null;
    #endregion
    #region Properties
    public override DefType DefinitionType
    {
      get { return DefType.Property; }
    }
    public virtual MethodRef Getter
    {
      get
      {
        return this.getter;
      }
      set
      {
        this.getter = value;
      }
    }
    public virtual MethodRef Setter
    {
      get
      {
        return this.setter;
      }
      set
      {
        this.setter = value;
      }
    }
    public virtual bool HasGetter
    {
      get
      {
        return this.Getter != null;
      }
    }
    public virtual bool HasSetter
    {
      get
      {
        return this.Setter != null;
      }
    }
    public virtual PropertyAttributes Attributes
    {
      get
      {
        return this.attributes;
      }
      set
      {
        this.attributes = value;
      }
    }
    public virtual bool HasDefault
    {
      get
      {
        return ((this.Attributes & PropertyAttributes.HasDefault) != PropertyAttributes.None);
      }
      set
      {
        if (value)
        {
          this.attributes |= PropertyAttributes.HasDefault;
        }
        else
        {
          this.attributes &= (~PropertyAttributes.HasDefault);
        }
      }
    }
    public virtual bool IsSpecialName
    {
      get
      {
        return ((this.Attributes & PropertyAttributes.SpecialName) != PropertyAttributes.None);
      }
      set
      {
        if (value)
        {
          this.attributes |= PropertyAttributes.SpecialName;
        }
        else
        {
          this.attributes &= (~PropertyAttributes.SpecialName);
        }
      }
    }
    public virtual bool IsRTSpecialName
    {
      get
      {
        return ((this.Attributes & PropertyAttributes.RTSpecialName) != PropertyAttributes.None);
      }
      set
      {
        if (value)
        {
          this.attributes |= PropertyAttributes.RTSpecialName;
        }
        else
        {
          this.attributes &= (~PropertyAttributes.RTSpecialName);
        }
      }
    }
    public virtual object DefaultValue
    {
      get
      {
        return this.defaultValue;
      }
      set
      {
        this.defaultValue = value;
      }
    }
    public virtual TypeRef PropertyType
    {
      get
      {
        return this.propertyType;
      }
      set
      {
        this.propertyType = value;
      }
    }
    public virtual bool CanRead
    {
      get
      {
        return this.HasGetter;
      }
    }
    public virtual bool CanWrite
    {
      get
      {
        return this.HasSetter;
      }
    }
    public virtual List<TypeRef> OptionalCustomModifiers
    {
      get
      {
        return this.optionalCustomModifiers == null
          ? this.optionalCustomModifiers = new List<TypeRef>()
          : this.optionalCustomModifiers;
      }
    }
    public virtual List<TypeRef> RequiredCustomModifiers
    {
      get
      {
        return this.requiredCustomModifiers == null
          ? this.requiredCustomModifiers = new List<TypeRef>()
          : this.requiredCustomModifiers;
      }
    }
    public virtual List<ParameterDef> Parameters
    {
      get
      {
        return this.parameters == null
          ? this.parameters = new List<ParameterDef>()
          : this.parameters;
      }
    }
    #endregion
    #region Constructors
    public PropertyDef(PropertyInfo propertyInfo):this(String.Empty)
    {
      this.OnReflecting(propertyInfo);
    }
    public PropertyDef(string name):base(name)
    {

    }
    public PropertyDef(string name, PropertyAttributes attributes)
      : this(name)
    {
      this.attributes = attributes;
    }
    public PropertyDef(string name, PropertyAttributes attributes, Type propertyType)
      : this(name, attributes)
    {
      this.propertyType = TypeRef.of(propertyType);
    }
    public PropertyDef(string name, PropertyAttributes attributes, Type propertyType, ParameterInfo[] parameters)
      : this(name, attributes, propertyType)
    {
      if (parameters != null)
      {
        this.ReloadParameters(parameters,this.Parameters);
      }
    }
    public PropertyDef(string name, PropertyAttributes attributes, Type propertyType, ParameterDef[] parameters)
      : this(name, attributes, propertyType)
    {
      if (parameters != null)
      {
        this.Parameters.AddRange(parameters);
      }
    }

    #endregion
    #region Methods
    #region Publics
    public virtual ParameterDef DefineParameter(string name)
    {
      return this.AddParameter(ParameterDef.of(name));
    }
    public virtual ParameterDef FindParameter(string name)
    {
      if (!string.IsNullOrEmpty(name) && this.parameters != null)
      {
        foreach (ParameterDef parameter in this.parameters)
        {
          if (parameter != null && parameter.Name == name)
          {
            return parameter;
          }
        }
      }
      return null;
    }
    public virtual ParameterDef RemoveParameter(string name)
    {
      return this.RemoveParameter(this.FindParameter(name));
    }
    public virtual TypeRef AddOptionalType(TypeRef type)
    {
      if (type != null)
      {
        this.OptionalCustomModifiers.Add(type);
      }
      return type;
    }
    public virtual TypeRef RemoveOptionalType(TypeRef type)
    {
      if (type != null)
      {
        this.OptionalCustomModifiers.Remove(type);
      }
      return type;
    }
    public virtual TypeRef AddRequiredType(TypeRef type)
    {
      if (type != null)
      {
        this.RequiredCustomModifiers.Add(type);
      }
      return type;
    }
    public virtual TypeRef RemoveRequiredType(TypeRef type)
    {
      if (type != null)
      {
        this.RequiredCustomModifiers.Remove(type);
      }
      return type;
    }
    #endregion
    #region Internals
    protected virtual ParameterDef AddParameter(ParameterDef parameter)
    {
      if (parameter != null)
      {
        parameter.Parent = this;
        this.Parameters.Add(parameter);
      }
      return parameter;
    }
    protected virtual ParameterDef RemoveParameter(ParameterDef parameter)
    {
      if (parameter != null)
      {
        parameter.Parent = null;
        this.Parameters.Remove(parameter);
      }
      return parameter;
    }
    protected override AnyGenerator OnCompleting(AnyGenerator generator)
    {
      if (generator is PropertyGenerator)
      {
        if (this.HasGetter)
        {
          ((PropertyGenerator)generator).SetGetter(this.Getter.ReflectInfo as MethodBuilder);
        }
        if (this.HasSetter)
        {
          ((PropertyGenerator)generator).SetSetter(this.Setter.ReflectInfo as MethodBuilder);
        }
        ((PropertyGenerator)generator).DefaultValue = this.DefaultValue;
      }
      return base.OnCompleting(generator);
    }
    protected override AnyGenerator OnDefining(AnyGenerator container)
    {
      if (container is TypeGenerator)
      {
        return ((TypeGenerator)container).DefineProperty(this.Name, 
          this.Attributes, 
          this.PropertyType.ReflectInfo, 
          this.GetParameterTypes());
      }
      return base.OnDefining(container);
    }
    protected virtual Type[] GetParameterTypes()
    {
      Type[] types = new Type[this.Parameters.Count];
      for (int i = 0; i < types.Length; i++)
      {
        ParameterDef header = this.Parameters[i] as ParameterDef;
        if (header != null)
        {
          types[i] = header.ParameterType.ReflectInfo;
        }
      }
      return types;
    }
    protected override ReflectDef OnReflecting(object source)
    {
      if (source is PropertyInfo)
      {
        PropertyInfo pi = source as PropertyInfo;
        this.attributes = pi.Attributes;
        this.propertyType = TypeRef.of(pi.PropertyType);

        this.getter = MethodRef.of(pi.GetGetMethod());
        this.setter = MethodRef.of(pi.GetSetMethod());

        this.ReloadParameters(pi.GetIndexParameters(), this.Parameters);
        this.ReloadMembers(pi.GetOptionalCustomModifiers(), this.OptionalCustomModifiers);
        this.ReloadMembers(pi.GetRequiredCustomModifiers(), this.RequiredCustomModifiers);
      }
      return base.OnReflecting(source);
    }
    protected override void DisposeList(bool disposing)
    {
      if (disposing)
      {
        this.DisposeList(this.Parameters);
        this.DisposeList(this.OptionalCustomModifiers);
        this.DisposeList(this.RequiredCustomModifiers);
      }
    }
    protected override void OnBreakingReference()
    {
      this.Break(this.PropertyType);
      this.Break(this.Setter);
      this.Break(this.Getter);
      this.Break(this.Parameters);
      this.Break(this.OptionalCustomModifiers);
      this.Break(this.RequiredCustomModifiers);
      base.OnBreakingReference();
    }
    #endregion
    #endregion
  }
}
